#ifndef _ColorMap_h__
#define _ColorMap_h__

#include <buildspec.h>
#include <config.h>

//use of color
#include <Geometry/IGeometry.h>

#include <map>

namespace GST {
namespace Utils {
	//prototypes:
	class Colormap;


//-----------------------------------------------------------------------------------------------------------

//some helpers for a map with floating point keys:

struct SafeDoubleLess
{
	bool operator()(double left, double right) const
	{
		bool leftNaN = (left != left);
		bool rightNaN = (right != right);
		if (leftNaN != rightNaN)
			return leftNaN<rightNaN;
		return left<right;
	}
};

/*
template<typename Container>
auto my_equal_range( Container&& container, double target, double epsilon = 0.00001 ) -> decltype( container.equal_range(target) )
{
	auto lower = container.lower_bound( target-epsilon );
	auto upper = container.upper_bound( target+epsilon );
	return std::make_pair(lower, upper);
}

template<typename Container>
bool key_exists( Container&& container, double target, double epsilon = 0.00001 )
{
	auto range = my_equal_range(container, target, epsilon);
	return range.first == range.second;
}
*/

//-----------------------------------------------------------------------------------------------------------

/**
  * Represents one segment in the color path trough the color cube. The segment is represented by it's endpoint (toValue with toColor).
  * Getting the lower bound use the segement before by preSegment() method.
  * The first segment in path is a virtual segment with a theoretical value range from -inf to first value of path. The path is stored in
  * member Colormap::m_path
  *
  * @see Colormap 
  */
class ColormapSegment
{

	friend class Colormap;

public:
	ColormapSegment(Colormap * parent, const float & segmentUpperValue, const Geometry::IGeometry::Color & segementUpperColor);
	ColormapSegment * preSegment() const;
	ColormapSegment * postSegment() const;
	float getUpperValue() const;
	Geometry::IGeometry::Color getUpperColor() const;

protected:
	Colormap * m_parent;
	float m_toValue;
	Geometry::IGeometry::Color m_toColor;
};
typedef boost::shared_ptr<ColormapSegment> ColormapSegmentPtr;
typedef std::map<float, ColormapSegmentPtr, SafeDoubleLess> ColorSegmentMap;

//-----------------------------------------------------------------------------------------------------------

typedef boost::shared_ptr<Colormap>	ColormapPtr;

class Colormap
{

	friend class ColormapSegment;

public:

	typedef ColorSegmentMap::const_iterator const_iterator;

	///@name constructors
	//@{
	Colormap();
	//@}

	enum GetColor_Result {
		VALUE_IS_SMALLER_THAN_FIRST_VALUE = -1, ///< color was set to the lower bound color of the colormap
		VALUE_IS_INTERPOLATED, ///< color was interpolated due to it's position in colormap
		VALUE_IS_GREATER_THAN_LAST_VALUE, ///< color was set to upper bound color of the colormap
		VALUE_IS_NO_DATA_VALUE ///< color was set to no data value
	};

	const_iterator begin() const;
	const_iterator end() const;

	std::size_t size() const;

	/**
	  * Calculate the color for the given value and write the color to parameter out_color.
	  */
	GetColor_Result getColor(Geometry::IGeometry::Color & out_color, const float & value);

	/**
	  * Inserts a ColormapSegment in the Colormap path at the end.
	  *
	  * @pre segment's upperValue has to be greater than the upperValue of the last segment in m_path (last inserted segment).
	  * @throws exceptions::GSTRuntimeException on precondition fail
	  */
	void push_back(float toValue, const Geometry::IGeometry::Color & toColor);

	/**
	  * Inserts a ColormapSegment in the Colormap path.
	  */
	void push(float toValue, const Geometry::IGeometry::Color & toColor);

	void setNodataColor(const Geometry::IGeometry::Color & nodataColor);

	Geometry::IGeometry::Color getNodataColor() const;

	/**
	  * @returns The segment the value is related to.
	  */
	ColormapSegmentPtr findSegment( const float & value ) const;

	static void Interpolate(Geometry::IGeometry::Color & out_color,
	                        const float & interpretationValue,
	                        const Geometry::IGeometry::Color & lowerColor,
	                        const float & lowerValue,
	                        const Geometry::IGeometry::Color & upperColor,
	                        const float & upperValue);
	
protected:

	/**
	 * key: upper bound of the (segment) interval, value: segment 
	 *
	 * The first segment in path is a virtual segment with a theoretical value range from -inf to first value of path
	 */
	ColorSegmentMap m_path;

	Geometry::IGeometry::Color m_nodataValueColor;
};



} //namespace Utils
} //namespace GST

#endif // _ColorMap_h__